package net.facelib.jni;

import static com.gitee.l0km.com4j.base.ConditionChecks.checkArgument;
import static com.gitee.l0km.com4j.base.ConditionChecks.checkNotNull;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class SdkRuntime implements RuntimeParamAccessor {
	private HashSet<String> availableParamNames = new HashSet<>();
	public SdkRuntime() {
	}
	public SdkRuntime(Iterable<String> iterable) {
		setAvailableParamNames(iterable);
	}
	public SdkRuntime(Class<? extends Enum<?>> enumClass) {
		setAvailableParamNames(enumClass);
	}
	public interface RuntimeConsumer{
		/**
		 * 设置SDK运行时参数<br>
		 * 参数名为null时抛出{@link IllegalArgumentException}异常<br>
		 * 参数值为null时使用默认值
		 * @param name 参数名
		 * @param value 参数值,数据类型必须参数名的要求
		 * @return 当前对象
		 * @throws SdkRuntimeException 参数设置错误
		 */
		RuntimeConsumer setRuntimeParam(String name,Object value) throws SdkRuntimeException;
	}
	/**
	 * 全局SDK运行参数
	 */
	private final HashMap<String, Object> globalRuntimeParams = new HashMap<>();	
	private final ReentrantReadWriteLock globalParamLock = new ReentrantReadWriteLock();
	/**
	 * 当前线程的SDK运行参数
	 */
	private final static ThreadLocal<Map<String, Object>> localRuntimeParams = new ThreadLocal<>();

	@Override
	public SdkRuntime setGlobalRuntimeParam(String name,Object value){
		checkParamName(name);
		globalParamLock.writeLock().lock();
		try {
			globalRuntimeParams.put(name, value);	
		} finally {
			globalParamLock.writeLock().unlock();
		}		
		return this;
	}

	@Override
	public SdkRuntime setLocalRuntimeParam(String name,Object value){
		checkParamName(name);
		Map<String, Object> m = localRuntimeParams.get();
		if(m == null){
			m = new HashMap<>();
			localRuntimeParams.set(m);
		}
		m.put(name, value);
		return this;
	}

	@Override
	@SuppressWarnings("unchecked")
	public <T> T getRuntimeParam(String name, T defaultValue){
		checkParamName(name);
		Map<String, Object> m = localRuntimeParams.get();
		Object value;
		if(null != m && null != (value = m.get(name))){
			return (T) value;
		} else { 
			globalParamLock.readLock().lock();
			try {
				value = (T) globalRuntimeParams.get(name);
				return null == value ? defaultValue : (T)value;
			} finally {
				globalParamLock.readLock().unlock();
			}
		}
	}

	@Override
	public SdkRuntime removeLocalRunTimeParam(){
		localRuntimeParams.remove();
		return this;
	}

	@Override
	public void setInstanceParam(RuntimeConsumer instance,String name){
		checkParamName(name);
		Map<String, Object> m = localRuntimeParams.get();
		if(null != m && m.containsKey(name)){
			instance.setRuntimeParam(name, m.get(name));
		} else { 
			globalParamLock.readLock().lock();
			try {
				if(globalRuntimeParams.containsKey(name)){
					instance.setRuntimeParam(name, globalRuntimeParams.get(name));
				}
			} finally {
				globalParamLock.readLock().unlock();
			}
		}
	}
	private String checkParamName(String name){		
		checkNotNull(name,"name is null");
		if(!availableParamNames.isEmpty()){
			checkArgument(availableParamNames.contains(name), "INVALID parameter name %s ,[%s] ", name);
		}
		return name;
	}
	public synchronized SdkRuntime setAvailableParamNames(Iterable<String> iterable){
		if(null != iterable){
			availableParamNames.clear();
			for(Iterator<String> itor = iterable.iterator();itor.hasNext();){
				availableParamNames.add(itor.next());
			}
		}
		return this;
	}
	public synchronized SdkRuntime setAvailableParamNames(Class<? extends Enum<?>> enumClass){
		if(null != enumClass){
			Enum<?>[] constants = enumClass.getEnumConstants();
			availableParamNames.clear();
			for(Enum<?> c:constants){
				availableParamNames.add(c.name());
			}
		}
		return this;
	}
}
